// TODO
// create some sort of layer
// create some sort of way to save layers
// create some sort of way to move layers
// create some sort of way to draw to a layer
// create some sort of way to save what was drawn to a layer
// update the refresh function to draw what is saved in the layer function
// optimize the refresh function to only redraw dirty things.
//===========================================================

/*jslint browser: true, undef: true*/
/*global requestAnimationFrame*/
function doodler(canvas) {
  // define variables.
  var DOODLER = { STOPPED: 0, RUNNING: 1 },
    _context = canvas.getContext('2d'),
    _state = DOODLER.STOPPED;

  // define properties.
  this.isArray = isArray;
  this.isDefined = isDefined;
  this.isFunction = isFunction;
  this.isObject = isObject;
  this.isString = isString;
  this.setCanvasSize = setCanvasSize;
  this.startDoodling = startDoodling;
  this.stopDoodling = stopDoodling;

  // define functions.
  /**
   * Clears the canvas of all data.
   * @param  {boolean} save True if the canvas should save its transformations and restore them.
   * @return {null}
   */
  function clearCanvas(save) {
    // save and reset the current state of the canvas.
    if (save) {
      // save the current transform.
      _context.save();

      // use the identity matrix while clearing the canvas.
      _context.setTransform(1, 0, 0, 1, 0, 0);
    }

    // clear the canvas.
    _context.clearRect(0, 0, canvas.width, canvas.height);

    // restore the previous state of the canvas.
    if (save) {
      _context.restore();
    }
  }

  /**
   * Checks if an object is an array.
   * @param  {Object}  object The object to test.
   * @return {Boolean} True if the object is an array.
   */
  function isArray(object) {
    return Object.prototype.toString.call(object) === '[object Array]';
  }

  /**
   * Checks if an object is defined.
   * @param  {Object}  object The object to test.
   * @return {Boolean} True if the object is defined.
   */
  function isDefined(object) {
    return object !== undefined;
  }

  /**
   * Checks if an object is an function.
   * @param  {Object}  object The object to test.
   * @return {Boolean} True if the object is a function.
   */
  function isFunction(object) {
    return typeof object === 'function';
  }

  /**
   * Checks if an object is an object.
   * @param  {Object}  object The object to test.
   * @return {Boolean} True if the object is an object.
   */
  function isObject(object) {
    return Object.prototype.toString.call(object) === '[object Object]';
  }

  /**
   * Checks if an object is an string.
   * @param  {Object}  object The object to test.
   * @return {Boolean} True if the object is an string.
   */
  function isString(object) {
    return typeof object === 'string';
  }

  // what should a layer do
  // it should contain a list of all objects that will be drawn to a screen
  // each of these objects will be at a coordinate
  // it should contain 

  /**
   * Refreshes the canvas.
   * @return {null}
   */
  function refreshCanvas() {
    // clear the canvas
    clearCanvas(true);
  }

  /**
   * Sets the canvas height and width.
   * @param {int} height The height of the canvas.
   * @param {int} width  The width of the canvas.
   */
  function setCanvasSize(height, width) {
    canvas.height = height;
    canvas.width = width;
  }

  /**
   * Sets the state of the doodler state machine.
   * @param {int} state The state of the doodler.
   */
  function setState(state) {
    if (DOODLER.hasOwnProperty(state)) {
      _state = state;
      return true;
    }
    return false;
  }

  /**
   * Starts the doodling state machine.
   * @return {null}
   */
  function startDoodling() {
    switch (_state) {
    case DOODLER.RUNNING:
      break;
    case DOODLER.REFRESHING:
      refreshCanvas();
      setState(DOODLER.RUNNING);
      break;
    default:
      return stopDoodling();
    }

    // request the next animation frame.
    requestAnimationFrame(startDoodling);
  }

  /**
   * Stops the doodling state machine.
   * @return {null}
   */
  function stopDoodling() {
    return setState(DOODLER.STOPPED);
  }
}

// requestAnimationFrame Polyfill by Erik Möller; fixes by Paul Irish and Tino Zijdel.
(function () {
  var lastTime = 0,
    vendors = ['ms', 'moz', 'webkit', 'o'],
    index = 0;

  for (index = 0; index < vendors.length && !window.requestAnimationFrame; ++index) {
    window.requestAnimationFrame = window[vendors[index] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[index] + 'CancelAnimationFrame'] ||
      window[vendors[index]] + 'CancelRequestAnimationFrame';
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function (callback, element) {
      var currentTime = new Date().getTime(),
        timeToCall = Math.max(0, 16 - (currentTime - lastTime)),
        timeoutId = window.setTimeout(function () { callback(currentTime + timeToCall); },
          timeToCall);
      lastTime = currentTime + timeToCall;
      return timeoutId;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function (id) {
      clearTimeout(id);
    };
  }
}());